﻿using System;
using System.IO;
using System.Linq;
using Org.Flowertwig.Media.Information.Services;
using Raven.Client.Indexes;

namespace Org.Flowertwig.Media.Services.LocalService
{
    class Program
    {
        const string prepareCommand = "/prepare:";
        const string copyCommand = "/copy:";
        const string cleanCommand = "/clean";
        const string resetCommand = "/reset";

        static void Main(string[] args)
        {
            // If we have no arguments, lets start service
            if (args.Length == 0)
            {
                var service = new IndexingService();
                service.Start();

                Console.Read();
                service.Stop();
            }else
            {
                foreach (var arg in args)
                {
                    if (arg.StartsWith(prepareCommand, StringComparison.CurrentCultureIgnoreCase))
                    {
                        PrepareMedia(arg);
                    }
                    else if (arg.StartsWith(copyCommand, StringComparison.CurrentCultureIgnoreCase))
                    {
                        CopyMedia(arg);
                    }
                    else if (arg.StartsWith(cleanCommand, StringComparison.CurrentCultureIgnoreCase))
                    {
                        CleanOutput(Properties.Settings.Default.OutputPath, DateTime.Now.AddDays(-2));
                    }
                    else if (arg.StartsWith(resetCommand, StringComparison.CurrentCultureIgnoreCase))
                    {
                        Reset();
                    }
                    else
                    {
                        Console.WriteLine("Unknown command: " + arg);
                    }
                }
            }
        }

        private static void Reset()
        {
            Console.WriteLine("# Reset - Creating Index");
            Storage.AddIndex("Medias/DeleteIndexes",
                                      new IndexDefinitionBuilder<Information.Media>
                                      {
                                          Map =
                                              myClasses =>
                                              from myClass in myClasses
                                              select new
                                              {
                                                  Id = myClass.Id
                                              }
                                      });

            Console.WriteLine("# Reset - Removing items matching index");
            Storage.DeleteAllItemsOfType<Information.Media>("Medias/DeleteIndexes");
            Console.WriteLine("# Reset - Items has been removed");

            Console.WriteLine("# Reset - Cleaning Output");
            CleanOutput(Properties.Settings.Default.OutputPath, DateTime.Now.AddDays(-2));
            Console.WriteLine("# Reset - Output has been cleaned");
        }

        private static void CopyMedia(string arg)
        {
            var value = arg.Substring(copyCommand.Length);

            string id = Hash.CreateId(value);
            string output = string.Format("{0}{2}{1}{2}", Properties.Settings.Default.OutputPath, id,
                                          Path.DirectorySeparatorChar);

            if (!Directory.Exists(output))
            {
                Directory.CreateDirectory(output);
            }

            var info = new FileInfo(value);
            var filename = output + info.Name;

            // TODO: Check if media has been copied already.
            if (File.Exists(output + "flowmedia-status.txt"))
            {
                var content = File.ReadAllText(output + "flowmedia-status.txt");
                if (string.Equals("OK", content))
                {
                    Console.WriteLine("PROCESS:" + 100); // 100%
                    return;
                }

                double storedSize;
                if (double.TryParse(content, out storedSize))
                {
                    var outputFile = new FileInfo(filename);
                    /******************
                     * Sadly OS is caching data about our file so we need to refresh the information.
                     * http://msdn.microsoft.com/en-us/library/system.io.fileinfo.name.aspx
                     ******************/
                    outputFile.Refresh();

                    double size = outputFile.Length;

                    Console.WriteLine("PROCESS:" + (size/storedSize)*100);
                    return;
                }
            }

            /******************
             * Sadly OS is caching data about our file so we need to refresh the information.
             * http://msdn.microsoft.com/en-us/library/system.io.fileinfo.name.aspx
             ******************/
            info.Refresh();
            File.WriteAllText(output + "flowmedia-status.txt", info.Length.ToString());
            Console.WriteLine("PROCESS:0");

            // TODO: This should be threaded some how. We don't want to wait until this action has been finished.
            File.Copy(value, filename);
        }

        private static void CleanOutput(string outputPath, DateTime oldestAllowedDate)
        {
            if (Directory.Exists(outputPath))
            {
                var dirs = Directory.GetDirectories(outputPath);

                foreach (var dir in dirs)
                {
                    var lastModified = Directory.GetLastWriteTime(dir);
                    if (lastModified <= oldestAllowedDate)
                    {
                        try
                        {
                            Directory.Delete(dir, true);
                        }
                        catch (Exception)
                        {
                            // TODO: Do something if any error occures
                        }
                    }
                }


                var files = Directory.GetFiles(outputPath);
                foreach (var file in files)
                {
                    var lastModified = File.GetLastWriteTime(file);
                    if (lastModified <= oldestAllowedDate)
                    {
                        try
                        {
                            File.Delete(file);
                        }
                        catch (Exception)
                        {
                            // TODO: Do something if any error occures
                        }
                    }
                }

            }
        }

        private static void PrepareMedia(string arg)
        {
            var value = arg.Substring(prepareCommand.Length);
            //Console.WriteLine("Prepare file:" + value);

            var archiveHandler = new ArchiveHandler(Properties.Settings.Default.SevenZipPath);

            var filters = new[] { ".*\\.avi$", ".*\\.mkv$" };
            //Console.WriteLine("Progress: " + archiveHandler.UnpackArchive(value, filters));
            Console.WriteLine("PROCESS:" + archiveHandler.UnpackArchive(value, filters));
        }
    }
}
